home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-09-17 | 67.1 KB | 1,363 lines | [TEXT/MPS ] |
- { Blat:
- a dcmd to use the MMU to protect the low 256 bytes of RAM from stray use.
- There is no data there, so any program writing (or reading) there is suspect. This is an
- even better, better bus error, since it will catch writes to 0, as well as reads from
- zero, and anything up to 255.
- Down side: You lose a bit of performance when this is installed. I set the MMU table
- size to be 256 bytes, and the extra overhead costs some performance. Overall, the
- machine is not noticeably slower. I change VBR, install my own MMU tables. These
- are in use even when Blat is turned off. I would leave it installed only when actively
- testing, and remove it when not, just as a conservative approach. This is a fairly weird
- hunk of code, because it is so close to the machine.
-
- Caveats:
- Sorry, but it won't work on a IIci, or IIsi. The onboard video causes grief, and the
- Ram is wildly mapped, even when it is in use. Requires a special case to fix it.
- The other bad thing is that it won't work on 040 machines. The MMU is so different
- that my tables won't work, so it would require another special case.
-
-
-
- Copyright © 1991 by Apple Computer, Inc., all rights reserved.
-
- by Bo3b Johnson 8/28/91
- MS 37-DS
- for best viewing, use Palatino 12.
-
- 8/28/91: First version of the program started from a skanky prototype. Most
- of the really hard problems were solved there.
- 10/4/91: First real release to people. Version 2. Includes the Friedlander
- interface additions, and tested on an fx and IIx. Thanks, Jim, it's way
- easier to use now.
- 10/16/91: Version 3. Fixed bug in 32 bit tables, that would map 1 Meg hunk at
- 8 Meg and 9 Meg to the same place.
- Fixed macsbug not being able to set breakpoints because I left protection
- on when I invoked it with DebugStr. Now leave protection off.
- Changed table in 32 bit mode to have all DT=1 entries, since Microsoft Word
- had a bus error that was being handled by the system. I had way high ram
- marked as invalid dt=0, for $1000 0000, and that made my MMU check fail
- in BusErrorGlue. Now set dt=1, it is handled by a system bus error handler.
- Commented out starting ROM addresses, since they are fx specific.
- 10/23/91: Version 4: Fixed bug calling Gestalt early in boot, that would give
- gestaltUndefSelectorErr, and I'd say VM was installed when it wasn't. Now
- use extra AND to case on that error, and assume VM isn't installed if I get that.
- Added code to protect the VBR page too, so that I can bust Macsbug from taking
- over the bus error vector. Check for VBR page validity too.
- Bumped up possible ignores to 200.
- 10/30/91: Version 5: Fixed bug introduced by locking the VBR page too, that of having
- Macsbug not be able to install an A-trap handler. Now allows all through
- except for writes to the bus error vector, which is saved in a global.
- Finally forced to look for Macsbug active using the MacJmp flag, since at boot
- time Macsbug does its init stuff without having NMI blocked.
- Cleaned up a few variables and routines that weren't necessary anymore,
- because of the new 24-bit table reusing 32-bit table, and VBR installing in
- the system heap.
- Killed the Get/Set VBR/TC routines, since I do it all in one routine now that
- is called SetMMURegisters. This is superior, since I can change the CRP and
- TC without turning off the MMU, which will be necessary for IIci someday.
- }
-
- UNIT Blat;
-
- {
- Build this dude using the Build Menu, it has a make file.
-
- Things to do:
- The registers are partly fried by the DebugStr for display. Fixes?
- Clean it up a bit.
- Make it work in IIsi, IIci case.
- Bigger page size matter on speed?
- 040 case of walking the tables to find bits to hit, spiffy bus error handler.
-
- Seem to have trouble with macsbug Log still, get a hang, waiting in syncwait.
- Incompatible with DAL? Was problem on IIx, don't know why.
- Not particularly pleased with the init code, it seems really ragged and should be
- modularized in a nicer form.
- Maybe not save the bus error handler in a global, use 8 instead? This would
- require turning off protection to get it.
- Is VBR based handlers not being a chain OK? ie. I force it to always be me.
-
-
- Questions:
- Do I need to have a semaphore to prevent reentry? I had one as an experiment in
- the prototype, but the bug turned out to be something else. I've removed it here,
- under the assumption that it is unnecessary. It was an attempt to prevent the
- handler from freezing Macsbug, by trying to break when Macsbug was invoked
- to handle a real bus error. I solved that by looking at NMIFlag instead.
- Should I use the Macsbug flag instead of the NMIFlag to determine if Macsbug is
- running? It's maybe more accurate, but it's in two places depending upon 24/32 bit
- mode. Also, I'm pretty darn sure that NMIFlag is always set when Macsbug is
- running, to prevent NMIs inside of Macsbug.
- If I don't turn off the protection while you are in Macsbug, the user can't use or
- see that memory, but nothing bad happens. Should I set it that way too, just
- for safety? Currently it is always on when I invoke Macsbug. If you set a
- breakpoint, or break in some other way, it is still active too.
- When the TC register disables the MMU, RAM is mapped straight through. On a
- IIci this would be bad for example, since the video is mapped at zero. This should
- ensure that I won't get any bad jumps or anything while it is off.
- On a IIci class machine, I could drive the installed table instead, and articulate the
- branch if need be. This would enforce the system page size of 32K though, so
- that needs some thought. In the 040 case, the same is true, but need a really
- spiffy bus error handler.
- Couldn't decide whether the bus error handler needs to check for the address
- being larger than 255, and calling the old one if so. The way I fixed the Word
- problem was to make all pages valid so that the Invalid bit in the MMSR would
- not be set. Not sure if that was the right way.
-
-
- General:
- OK, one big problem here is that the dcmd cannot turn on the MMU stuff when
- it is called from Macsbug. As a hang example, when the Bus Error handler calls
- Macsbug via DebugStr, that starts up Macsbug. The first thing Macsbug does is
- try to change the bus error vector in low memory, so that it can handle its own
- bus errors. This invokes my bus error handler for a invalid write, where I call
- Macsbug... and you get the big wedge. I fixed it partly by watching for the NMIFlag
- to be on, which means that Macsbug is executing. When it is executing, I don't
- report any references to the zero page, since any DebugStr while Macsbug is
- executing will cause a hang. This was partly successful, but the next problem is
- that Macsbug calls the video-driver to swap video depth. That driver installs
- a bus error handler temporarily to catch it killing itself I guess. Anyway, this is
- after Macsbug has installed its own bus error handler, so when the video driver
- writes to $8, it invokes Macsbug's bus error handler, and that causes Macsbug to
- hang again, as it tries to display a message before it is fully operational. So....
- This leads to the use of the VBR register to remap the vectors to a different
- location. I currently put them in a block in the dcmd code space, which is way
- high in memory, and a smaller target. The trouble with this is that now any
- of the system wide bus error handlers that are installed are being installed
- zero based, not VBR based (the 68000 didn't have a VBR register, so the original
- code had to use zero based, and no one has paid attention to VBR yet.) Installed
- there, my bus error handler still takes precedence, and I can pass along any
- non-MMU error to the bus error handler at $8. This is what VM does too.
- I don't want to do it for all the vectors though, so any operations other than
- bus errors won't get fed through.
- Too bad I guess. There's nothing terribly important, but its something to note.
-
- OK, the definitive answer on whether I need to use VBR or not. The answer is
- yes, I must. The reason is that if I set the zero page (0..255) as invalid, with DT=0,
- then the cpu will halt with a double bus fault. It takes an interrupt say, that
- causes a fault, then it tries to get the bus error vector, which also causes a fault.
- Biff, you've got a locked machine. Ergo, I must use VBR, and it doesn't have
- anything to do with Macsbug weirdness. Currently, I shadow everthing up to the
- VBR page, as the active vector page, and leave the zero page locked. Both the
- zero page and the VBR page should be the same, except when some VBR
- knowledgable code like Macsbug sneaks in and changes the VBR page.
-
- And, definitively, I must check for Macsbug running and avoid calling DebugStr
- while it is active. Macsbug itself is OK since it uses VBR based handlers. It calls
- the video driver which tries to write to 8, which forces a bus error which makes
- Macsbug freak out with 'macsbug caused the exception'. If Macsbug left it's handler
- installed until after the video swap it would be OK, but it puts me back, then swaps
- video, causing me to invoke macsbug while it is still active. Hence, I put in the
- check for IsMacsbugActive to avoid the problem.
-
- The other part of the story is that dcmd's can't set VBR, since Macsbug saves
- and restores that value during it's switch. So the dcmd changes the real, active
- VBR, which is then crushed at Macsbug exit by the one saved in Macsbug's
- register file. I can get around this by installing all my stuff at dcmd init time,
- during boot. This is not fully desireable since I really wanted to leave the
- machine in a more pristine state when Blat wasn't turned on. The only real
- difference will be that Blat installs it's tables and sets up VBR and the MMU
- registers at dcmdInit, but leaves the protection itself turned off, so that the
- 0 page is marked as valid instead of write or read protected. This way the
- computer will use my tables during normal use, and turned on only when told
- so by the dcmd. The downside of this is that it will slow the computer by around
- 10%, since my tables are bigger than the systems (256 bytes/page, instead of 32k bytes).
- This probably means you don't want to leave Blat installed when you aren't
- actively using it, since it modifies the system at a fundamental level.
-
- An experiment that I started was to try to see if I could make the system work
- with a larger page size, like 4K. The idea was to set up the bus error handler to
- allow anything above 255 to go through, and only stop the 0..255 references. The
- test was to see if doing it that way with a bigger page would be slower or faster
- than having the 256 byte page size. The experiment failed in this environment,
- since Macsbug uses low memory globals, and would thus generate an internal
- bus error while it was running. That would make it try to say 'Macsbug caused
- the exception', which caused another bus error, and whammo you've got a well
- hung machine. I don't currently see any way around this, so I'm bagging the
- experiment for now.
-
- The VBR based page is being used by the CPU while Blat is running. This disables
- any short term bus error handlers that are installed at zero, which is bad because
- they are there to catch and handle weird errors, and to keep the system running.
- I want those to still be active so that Blat doesn't disable some of this error fixing
- code while it is installed. Note this would happen even if Blat wasn't turned on,
- since I have to install the VBR based table for reasons specified above (Macsbug
- will hang). So to avoid that problem and to make it more generally friendly, I
- will turn on write protection from the start, when it is installed, and any time I
- get a bus error I'll do the EmulateAccess so that that access will install into the
- 0 based page, as well as the VBR page. By doing this I can keep the pages in sync,
- so the system won't really notice that I'm doing a weird thing by moving VBR
- from 0. This doesn't give any safety to things since a bad access down there can
- smoke the valid VBR table now, but in general the system runs smoothly, and
- this is an informant type of tool instead of trying to enforce system rules. In
- other words, if it previously crashed the computer, it still will; but now you can use
- Blat to stop at invalid references so you can find the crashing bug immediately.
- When Blat is turned on, that will just set a global that decides whether I should
- stop or not, since the protection will already be on. Blat is thus never really
- off when it is installed. It will just not show bus errors that it catches. Another
- side of this problem is that I cannot just copy up the temporary bus error handlers
- that are installed while the system runs, since they would then be installed VBR
- based, and when they went to de-install themselves at 0, an MMU induced bus
- error would result, and their bus error handler would take it. I can't expect them
- to handle a bus error of this form, so as a hack I don't shadow writes to 8 up to
- VBR+8, since those are bus error handlers. This leaves my bus error handler in
- charge up there at VBR+8. Macsbug doesn't have a problem with this, since it
- always installs its handler at VBR+8, and thus avoids writing to 8 and catching
- the spurious errors. When macsbug is executing, my bus error handler is
- not installed, so you can't write to the zero page, even with macsbug.
-
- In order to keep those temporary bus error handlers active and working like they
- are supposed too, I need to pass along bus errors that aren't MMU related. This
- is for normal crash and burn type of bugs, that may be caught by a handler. The
- system is full of these temp install things, and I need to keep them working as
- expected, or the stability of the system with blat installed would be worse. In order
- to do this, when I take a bus error in my handler, I look to see if it is MMU related
- by doing a PTEST instruction. If that says it's an MMU error, then I'll go ahead
- and do the normal blat thing to see if I stop or let it go through. If it isn't an
- MMU induced bus error, then I just feed the error on to the handler that is
- installed at 8. That will be the current handler, and presumably knows how to
- handle the error. It can also call up the debugger in the case where it shouldn't
- have happened. Thus temporary handlers are available still, and the system
- should run as it normally does, except a bit slower due to the table overhead and
- the bus errors that don't cause stops.
-
- An interesting problem I ran into on a different machine was that the IIfx will
- run properly even if the RAM is in the wrong bank. The machine I looked at
- had 16 Meg in 4 Meg simms, but they were installed in Bank B instead of in
- Bank A as is customary. The ROM is smart enough to set up MMU page tables
- to map all the RAM back to memory location 0 from 128Meg where Bank B
- starts. This poses quite the problem for me however, since the RAM is
- physically installed way up there. When I turn off the MMU to remap it for
- my tables, the machine crashes, of course, since RAM is now 128 Meg away, and
- the PC is executing funny space. So... in case it doesn't work on your IIfx, look
- to see if your RAM is installed in Bank B instead, will make it impossible for
- me to run. (I don't think there is a solution to the problem, other than having
- real RAM in Bank A). As long as there is some RAM in Bank A, I should be fine.
-
- SwapMMUMode will flush the cpu caches when it changes mode. Specifically I
- don't need to do that, because the addresses and data that are cached are still
- valid after I set a new CRP with new tables. I don't change the mapping like
- SwapMMUMode, I only change the table types.
-
- Wow, what a surprise, another totally gnarly problem to try to solve. The problem
- occurs when protection is on, and you break into macsbug. Macsbug is then running
- with protection (either read or write) turned on, and anything that happens will
- make Macsbug barf. Macsbug has installed it's own bus error handler into the VBR
- based table, upon entry, as a way of catching errors and restarting it's 'Main Event
- Loop'. The trouble is, when you do something like Log, or even switching to the
- regular mac screen, some system code runs, which tries to install another bus
- error handler, zero based. That is busted by still on protection, and the current
- bus error handler being Macsbug-- gives the technicolor yawn. This doesn't have
- anything to do with my leaving the protection on from boot on up, since it will
- happen whenever the protection is active, like when Blat is doing its job. Waah.
- It's too hard. OK, so what I'm doing now is to try to lock down the bus error
- handler so that only I have control over the handler, and can't be replaced by
- other code, most specifically, Macsbug. I need to have a VBR based set of vectors,
- since I still need to allow for read protection, and you can't do read protection on
- the active vectors, as noted above. I can however do write protection on the VBR
- based vectors, which is what I'm doing to prevent Macsbug from coming in and
- stomping my vector. I need to take control first, and allow things to go on through
- as if I wasn't there, whenever Macsbug is active. This will prevent the type of
- bug where Macsbug catches a bus error that I should be handling. I'll still keep
- track of the current secondary bus error handler, and call it if it isn't an MMU
- protection error. Motorola really blew it by overloading the bus error vector. This
- would have been easy if they'd made a specific MMU bus error vector. Now, in
- order to protect the VBR vectors too, I need to have an entry in my tables to
- set the WP bit on. I don't want to change the tables too much, since they are hard
- to debug, so I'll make the restriction that the VBR page has to be allocated in the
- system heap in the first 64K, which is how far my table stretches for the fourth
- level in 32 bit mode. That's 256 entries of 256 bytes apiece. If I can allocate a block
- of Ram there, then I can align it to a page boundary, and set the WP bit, preventing
- anyone from changing it from underneath of me.
-
- As a sidelight, I thought about a different way to solve that problem, which was to
- try to catch whenever Macsbug would be activated, and turn off protection during
- those times. Most things go through MacJmp, so I could theoretically install my
- own MacJmp, turn it off, then fly on to macsbug, coming back, turn it back on, then
- back to the rom. I chose not to do it this way, because Macsbug has more than one
- entry vector. It will use the trace bit and the a-trap handler too, on occasion, making
- it further difficult. If I didn't trap those and whatever else I don't know about, then
- you could get into macsbug with the protection still active, and get the same problems
- as before. It would work mostly, but still have problems.
-
- A convenient modification I made to the tables made it unneccessary to have a full
- 4 levels on the 24-bit tables. I set up the third level in those tables to be the same as
- the 4th level for the 32-bit tables. That set of tables is an 8-bit hunk, so there are 256
- entries in the table, and that is mapping 256 byte page sizes so it works in both 24 and
- 32 bit mode. A side effect of this is that I no longer need to keep two places to turn
- protection off or on, since the same table is used in both modes. Now I just save the
- single address, and set the byte to yea/nay. I still have the VBR table too, since that
- was a new addition, and it is in that 8-bit table as well, but wherever it was allocated
- in Ram.
-
- I realized that I had done a dumb thing here, which was not setting up a chain of
- standard bus error handlers. I was saving the bus error handler off in a global, which
- of course meant I could only save a single handler, and any subsequent reads of
- memory location 8 would get the default Rom bomb handler. This was clearly a
- mistake as I found on the emulator, and so now I go ahead and just write the value
- on through to memory location 8, save it in a global, and any time I get a bus error
- that needs to feed through, then I'll just pass it along to that address. This effectively
- makes a chain of handlers, since the save/restore characteristic of other code in the
- system will read what's there, save it, then set it's own, then restore the old. If
- something in between does its own handler installation, it will read the prior level
- and everything will be fine. It will be a chain maintained in the locals of the code
- installing the temporary handlers.
-
- Sorry about the kMaxIgnores hard coded number for the number of PCs to ignore.
- Its one of those funny things that's hard to get around in a dcmd. You have to
- assume that the dcmd is invoked later at interrupt time, since that is how an NMI
- will get into macsbug. This means the heaps are in an unknown state, so you
- cannot legitimately allocate memory. You can work most of the time, but that's not
- good enough. This means it's really hard to have dynamic memory allocation in a
- dcmd. The simplest answer is to have a constant and just allocate it upfront during
- dcmdInit, when it IS safe to allocate memory. To be more polite, I should get a
- constant from a mx?? macsbug resource and thus allow it to be changed using resedit.
- Course, this is one of the main reasons I give out the source, so you can change the
- numbers and recompile easily.
- }
-
- {$R-}
- {$D+} { debug labels on. }
-
- INTERFACE
-
- USES MemTypes, Resources, Traps, Memory, ToolUtils, OSUtils, Events, SysEqu,
- GestaltEqu, dcmd; { Macsbug interface routines. }
-
-
- { Lower case starting letters means I don't use that identifier in the record.
- This frame is generated by the CPU on bus errors. }
- TYPE BusErrorFrame = Record
- SR: Integer;
- PC: LongInt;
- typeAndVector: Integer;
- internal1: Integer;
- SSW: Integer; { Special Status Word, info bits. }
- pipeC: Integer;
- pipeB: Integer;
- FaultAddress: Ptr; { Address that caused bus error. }
- internal2: Array [1..2] of Integer;
- OutputBuffer: LongInt; { Get bytes from here, for a write. }
- internal3: Array [1..4] of Integer;
- stageBaddress: LongInt;
- internal4: Array [1..2] of Integer;
- InputBuffer: LongInt; { Stuff bytes here, on a read. }
- internal5: Array [1..3] of Integer;
- version: Integer;
- internal6: Array [1..18] of Integer;
- End;
- BusErrorFramePtr = ^BusErrorFrame; { So I don't copy the record. }
-
- { For the CRP and TC combinations in both 24 and 32 bit mode, I make a record
- so that I can explicitly define their order in RAM. This is necessary because
- I install a pointer to these records (as global vars in the dcmd space), and that
- pointer is used by the system during SwapMMUMode. It must be the
- CRP register followed by the TC register. }
- MMURegisters = Record
- theCRP: Int64Bit;
- theTC: LongInt;
- End;
-
- { When I pass back hex numbers on the stack, I want to use small ones. }
- Str8 = String[8];
-
- { Vars that are used in other units, or outside of this unit need to be declared in the
- Interface section. This is a genuine global, so it is prefixed by the 'g'. The Z+ option
- says to make these available to the Linker, so that the Asm side of the world can
- see the global var. }
-
- {$PUSH} {$Z+}
- VAR { *** make access methods so I don't have to do this. }
- gSavedSR: Integer; { Save place for TurnInterruptsOff. }
- gStandardBusError: LongInt; { save off the currently installed bus error vector. }
- gStandardVBRBusError: LongInt; { save off the currently installed VR bus error vector. }
- {$POP}
-
- { Public declaration for dcmdGlue. Must be in every dcmd. The name cannot be changed. }
- PROCEDURE CommandEntry (paramPtr: dcmdBlockPtr);
-
- { This must be exported so that the assembly side can find this routine. }
- PROCEDURE BusError (theFrame: BusErrorFramePtr);
-
- IMPLEMENTATION
-
- CONST
- kShortDT = 2; { For use as DT=2. }
- kProtectionOff = $01; { DT=1 for valid page, no protection. }
- kWriteProtect = $05; { DT=1, but the WP bit is set. }
- kReadWriteProtect = $00; { DT=0, so the page is marked invalid. }
-
- kMMU24Info = MMUTbl; { make the better name from include file. }
- kMMU32Info = MMUTblSize; { 32 bit MMU table pointer in low memory.}
-
- kMaxIgnores = 200; { Maximum number of addresses to ignore. }
-
- kHexDigits = '0123456789ABCDEF'; { Digits in base 16, for hex conversion. }
- kUseIL = False;
- kUseIP = True;
- kOn = True;
- kOff = False;
-
- { Some don't
- really have to be globals, but it is a convenience. I label them with a p, so that you can
- immediately see they are private globals (to this unit), a quality MacApp convention.
- I use more globals than normal in dcmd's since Macsbug has such a wimpy stack. }
- VAR pDumpString: Str255; { To dump label info, from symbols in code. }
-
- pRegisters24: MMURegisters; { 24 bit CRP and TC values I install. }
- pRegisters32: MMURegisters; { 32 bit CRP and TC values I install. }
-
- pProtectAddress: Ptr; { byte that turns protection on and off. }
- pProtectValue: Byte; { current setting of Blat. }
-
- pIgnoreArray: Array [1..kMaxIgnores] of LongInt; { pc addresses to ignore as valid bus errors. }
- pIgnoreCount: Integer; { Number of valid ignore addresses. }
- pLearn: Boolean; { is learn mode on, or not}
- pAutoLearn: Boolean; {autolearn is silent, doesn't break into MacsBug}
- pUseIP: Boolean; { do we do IL or IP when we break}
-
- pDisplayError: Boolean; { Blat On/Off: to show bus errors or not. }
-
- pErrorString: Str255; { Error string if I can't install on the machine. }
-
- pVBRTable: Ptr; { table allocated in system heap for VBR vectors. }
- pVBRProtectAddress: Ptr; { Entry in tables for VBR page in system heap. }
-
- {---------------------------------------------------------------------------------------------------------------------------------}
-
- { Not really external as procedures, they are table data. This gives us a nice handy
- Pascal interface to get the address of each table, without having to pretend that
- the tables are code. }
- FUNCTION GetStartSpace: Ptr; EXTERNAL;
-
- FUNCTION GetFirstLevel32: Ptr; EXTERNAL;
- FUNCTION GetSecondLevel32: Ptr; EXTERNAL;
- FUNCTION GetThirdLevel32: Ptr; EXTERNAL;
- FUNCTION GetFourthLevel32: Ptr; EXTERNAL;
-
- FUNCTION GetFirstLevel24: Ptr; EXTERNAL;
- FUNCTION GetSecondLevel24: Ptr; EXTERNAL;
-
- FUNCTION GetEndOfTables: Ptr; EXTERNAL;
-
- PROCEDURE SaveTheA5; EXTERNAL;
- PROCEDURE SetVBR (vbrPointer: Ptr); EXTERNAL;
- FUNCTION GetVBR: LongInt; EXTERNAL;
- PROCEDURE FlushATC (address: Ptr); EXTERNAL;
-
-
- PROCEDURE SetMMURegisters (theRegisters: MMURegisters); EXTERNAL;
-
- PROCEDURE TurnInterruptsOff; EXTERNAL;
- PROCEDURE TurnInterruptsOn; EXTERNAL;
-
- PROCEDURE BusErrorGlue; EXTERNAL;
-
- FUNCTION FindMMUEntry (address: Ptr): Ptr; EXTERNAL;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Well, I stole this routine from MacApp utilities. I want to lower case the strings so I
- don't have case sensitivities. This will do it, without using the toolbox. }
- PROCEDURE LowerStr255(VAR s: Str255);
-
- VAR i: INTEGER;
-
- BEGIN
- FOR i := 1 TO LENGTH(s) DO
- IF (s[i] IN ['A'..'Z']) THEN
- s[i] := CHR(Ord(s[i]) + 32)
- END; { LowerStr255 }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Another handy routine stolen from MacApp to do the conversion on the dang strings. I
- only pass back Str8, since that is the maximum length, and stack space is limited in
- Macsbug, and I don't want to waste it needlessly.
- Notably, this one handles negative LongInts properly, unlike the one distributed with
- the dcmd samples. }
- FUNCTION NumberToHex(decNumber: UNIV LongInt): Str8;
-
- VAR i: Integer;
- hexNumber: Str8;
-
- BEGIN
- hexNumber[0] := CHR(8);
- FOR i := 8 DOWNTO 1 DO
- BEGIN
- hexNumber[i] := kHexDigits[BAND(decNumber, 15) + 1];
- decNumber := BSR(decNumber, 4);
- END;
- NumberToHex := hexNumber;
- END;
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { A utility routine to decide if I'm in 32 bit mode or not. This has to check the low
- memory global to see if the bit is set. }
- FUNCTION Is32BitMode: Boolean;
-
- VAR bitMode: Byte; { For convenient bit testing. }
-
- BEGIN
- bitMode := Ptr(MMU32Bit)^; { Get MMU32bit value. }
- IF BTst (bitMode , 0) THEN
- Is32BitMode := True
- ELSE
- Is32BitMode := False;
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { A utility routine to decide if Macsbug is running or not. If it is, I can't call things like
- DebugStr. This checks the low memory global NMIValue, since Macsbug sets it when
- it is entered. A potentially better choice is the MacJmp top byte which has flags to say
- a similar thing, but it is in two places, depending on bit mode.
- I've got the sort of reverse logic here because I wanted to make it easier to read
- where I use it.
- I have to set it up the way Macsbug turns itself on, since NMIFlag by itself is not
- good enough. At boot, Macsbug is active, but hasn't set it. There may be other times
- as well. I going to be conservative here, and just check both fields to see if it might
- active, on the assumption that it is safer, and has no drawback. }
- FUNCTION MacsbugIsNotActive: Boolean;
-
- CONST MacJmpFlag = $BFF; { Flags only defined in assembly. }
- MacJmp = $120; { Used in different bit mode. }
- kMacsbugActive = 7; { Bit in flags as debugger running. }
-
- VAR NMIValue: Byte;
- MacsbugFlags: Byte;
- Rom24Only: Boolean;
-
- BEGIN
- NMIValue := Ptr(NMIFlag)^;
- IF BTST (NMIValue, 7) THEN { Bit set means Macsbug is live. }
- MacsbugIsNotActive := False { And only do that check. }
- ELSE BEGIN
- { The NMIFlag is not set, so check the secondary characteristic of the MacJmp flags.
- If the machine is in 32 bit mode, use the MacJmpFlag to see. If it is in 24 bit mode,
- the flags are stored in the high byte of the MacJmp vector. God I love this compatibility
- programming. The MacJmpFlag is worse than that even. It turns out that it is $FF
- if the ROM is not capable of 32 bit mode. If it is capable, it is used as the flags,
- regardless of the bit mode. Ugh. This is how Macsbug checks, and since this is here
- strictly for Macsbug compatibility, I am doing it the same way. Sorry about the
- -1 as a magic number, but it isn't defined as a constant, it IS a magic number as
- used by Macsbug and the header files. }
- Rom24Only := Ptr(MacJmpFlag)^ = -1;
- IF Rom24Only THEN
- MacsbugFlags := Ptr(MacJmp)^
- ELSE
- MacsbugFlags := Ptr(MacJmpFlag)^;
-
- IF NOT BTST (MacsbugFlags, kMacsbugActive) THEN
- MacsbugIsNotActive := True
- ELSE
- MacsbugIsNotActive := False;
- END;
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { A routine to set the bytes for protection. This one byte is found in the 24 bit and 32
- bit MMU page tables. The same set of tables are used in both bit modes, with different
- TC values. This just sets the DT byte of that short format entries to be the
- protection specified in the global. The actual memory location is set up when the tables
- are initialized at InstallTables. The FlushATC is necessarly since the Address Translation
- Cache in the MMU will have cached the protection state for that page, and I just changed
- it. For the VBR page, I want to turn it back on, but it can never be kReadWrite or it's death.}
- PROCEDURE TurnProtectionOn;
-
- BEGIN
- pProtectAddress^ := pProtectValue; { restore the protection value. }
- pVBRProtectAddress^ := kWriteProtect; { Keep it from changing in VBR. }
- FlushATC (NIL);
- FlushATC (pVBRTable); { Flush zero page, and VBR page. }
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { A routine to set the bytes for protection. This does the counterpart of TurnProtectionOn,
- but of course disables the MMU protection for the benefit of my routines, primarily the
- EmulateAccess routine. I have to turn it off during my writes, to avoid getting a
- reentrant bus error. Naturally this just sets the DT=01 as a valid descriptor with no
- write protect. Turn off protection for the VBR based page too. }
- PROCEDURE TurnProtectionOff;
-
- BEGIN
- pProtectAddress^ := kProtectionOff;
- pVBRProtectAddress^ := kProtectionOff;
- FlushATC (NIL);
- FlushATC (pVBRTable); { Flush zero page, and VBR page. }
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { When called upon by the user, this routine will ignore the address that is passed in, by
- adding it to the ignores list; then bumping the ignore count. These addresses are
- skipped during bus error handling, as valid accesses. }
- PROCEDURE AddToIgnores (ignoreAddress: LongInt);
-
- BEGIN
- pIgnoreCount := pIgnoreCount + 1;
- IF pIgnoreCount > kMaxIgnores Then
- dcmdDrawLine (ConCat(' too many addresses to ignore, max: ', NumberToHex(kMaxIgnores)))
- ELSE BEGIN
- pIgnoreArray [pIgnoreCount] := ignoreAddress;
- IF pAutoLearn = kOn THEN
- dcmdDrawLine (ConCat(' Auto-learning address: ', NumberToHex (ignoreAddress)))
- ELSE IF pLearn = kOn THEN
- dcmdDrawLine (ConCat(' Learning address: ', NumberToHex (ignoreAddress)))
- ELSE
- dcmdDrawLine (ConCat(' Ignoring address: ', NumberToHex (ignoreAddress)));
- END;
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Dump the list of ignored values, so they can see what all is being ignored. }
- PROCEDURE DumpIgnoresList;
-
- Var I: Integer;
-
- BEGIN
- dcmdDrawLine (' Blat list of ignored addresses: ');
- For I := 1 to pIgnoreCount Do
- dcmdDrawLine (ConCat(' ', NumberToHex (pIgnoreArray[I])));
- End;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
-
- { When set up and running, I take bus errors that are due to the memory protection
- I've installed. This routine is the actual bus error handler, that is installed. It does
- several things, since Motorola blew it and overloaded the bus error routine. (eg. page
- faults should go through a different vector.) It will examine the frame to see if it is one
- generated by the MMU, and if so, will clear the re-run bit, to say I'll handle it. The
- page protection is turned off, the memory access is emulated, then the protection is
- turned back on, and the routine returns with an RTE to continue. }
-
-
- { EmulateAccess is to run the memory operation the CPU was trying to do when I busted
- it's chops by having the page protected. This is presumably a legitimate access to the
- zero page vectors, like you might see when the ROM installs a one-shot bus error
- handler. Emulate the access by figuring out the address, the data to move, the size,
- and which way to move it. All this info is in the bus error stack frame. The
- faultAddress is the memory location that caused the bus error, either as a read or
- as a write. theSSW is the Special Status Word, which has the flags describing the
- access. The outputBuffer is the right aligned data that is being written. The inputBuffer
- is only used when it is a read operation, and is a pointer to the location in the stack
- frame that needs the data.
-
- As part of making the machine run properly, since this has to be installed at boot, I
- want to allow the access to go through to both the 0 based access, as well as the VBR
- based table, so that short term bus error handlers are installed in the active place off
- of VBR. Nearly all the accesses are valid, and in any case, this is just trying to be a
- spy tool, not a police tool. Doing it this way makes ADB key work again, and any
- other funky system junk that need the vector page (like sound, via hardware). I
- could do a PTEST and pass the bus error on to the 0 based page, but that would
- special case the bus errors, and my goal is really just to inform the programmer,
- which will have already been done. For writes then, I'll write the data to both
- the zero based access that I caught, and will write it to VBR+offset. For reads, I'll
- just let it do the normal thing of getting it from zero, since VBR based vectors and
- 0 based vectors should always be in sync.
-
- More complication, of course. Since I now protect the VBR based page from writes,
- I can take bus errors there too, primarily from Macsbug. In fact, anything other than
- a write to vbr+8 is probably a bug, so I'll not even emulate those. If it is VBR+8, then
- I need to save the address, for passing along in case of a real bus error, but I still won't
- write it to the VBR page. }
- PROCEDURE EmulateAccess (theSSW: Integer; faultAddress: Ptr;
- outputBuffer: Ptr; inputBuffer: Ptr);
-
- CONST kReadBit = 6;
- kByteBit = 4; { If set, always a byte access. }
- kWordBit = 5; { If both 4&5 are clear, it's a long access. }
- kLong = 0;
- kWord = 1;
- kByte = 2; { constants for a Case. }
-
- VAR source, destination: Ptr;
- vbrDestination: Ptr; { for copying it there too. }
- doRead: Boolean;
- theSize: Integer; { Set up for a Case. }
- shadow: Boolean;
-
- BEGIN
- { Right up top, if this is an access above the 0..255, then it must be the VBR page only,
- since I don't want to shadow backwards from vbr to 0 based. }
- IF ORD(faultAddress) > 255 THEN
- shadow := False
- ELSE
- shadow := True;
-
- { Set up a couple of local vars that are easier to deal with than the BitTest stuff. }
- doRead := BTst (theSSW, kReadBit);
-
- IF BTst (theSSW, kByteBit) Then { Set up theSize. }
- theSize := kByte
- ELSE IF BTst (theSSW, kWordBit) Then
- theSize := kWord
- ELSE theSize := kLong; { I'm ignoring the 3-byte case. }
-
- { Now decide which of the two types it is, read or write. For Reads, I need to move
- the data from the fault address to the InputBuffer, which is in the stack frame itself.
- I have to move the data in a right-aligned fashion though, so for anything less
- than a long word, I need to bump up the destination pointer so it will be right
- aligned data. Then I need to move only the amount of data specified, since I
- could bus error again, for addresses on the edge. }
- IF doRead THEN BEGIN
- source := faultAddress; { For reads, go from fault to inputBuffer. }
- destination := inputBuffer;
- Case theSize Of
- kByte: BEGIN
- destination := Ptr(ORD(destination) + 3); { Bump pointer by 3 for right aligned. }
- destination^ := source^; { Move it only as a byte. }
- END;
- kWord: BEGIN
- destination := Ptr(ORD(destination) + 2); { Bump pointer by 2 for right aligned. }
- IntegerPtr(destination)^ := IntegerPtr(source)^;
- END;
- kLong: BEGIN
- LongIntPtr(destination)^ := LongIntPtr(source)^; { Move full 4 bytes across. }
- END;
- END; { Case theSize }
- END
-
- { For Write operations, I have the source data in the output buffer that is on the
- stack frame. That data needs to go to the fault address. The data in the output buffer
- is right aligned, so I need to bump the source pointer for everything except the
- long word access. Also, only move the exact size of data specified.
- I added the write to VBR based page too. }
- ELSE BEGIN
- source := outputBuffer; { For writes, move it from output to fault. }
- destination := faultAddress;
- vbrDestination := Ptr(ORD(faultAddress) + GetVBR); { make address in VBR based page. }
-
- Case theSize Of
- kByte: BEGIN
- source := Ptr(ORD(source) + 3); { Bump pointer by 3 for right aligned. }
- destination^ := source^; { Move it only as a byte. }
- IF shadow THEN vbrDestination^ := source^; { Move byte to VBR page too. }
- END;
- kWord: BEGIN
- source := Ptr(ORD(source) + 2); { Bump pointer by 2 for right aligned. }
- IntegerPtr(destination)^ := IntegerPtr(source)^;
- IF shadow THEN IntegerPtr(vbrDestination)^ := IntegerPtr(source)^;
- END;
- kLong: BEGIN
- { Don't copy new bus error handlers up there. }
- IF ORD(destination) = ORD(pVBRTable)+BusErrVct THEN
- gStandardVBRBusError := LongIntPtr(source)^
- ELSE IF ORD(destination) = BusErrVct THEN BEGIN
- gStandardBusError := LongIntPtr(source)^;
- LongIntPtr(destination)^ := LongIntPtr(source)^; { and into low memory too. }
- END
- ELSE BEGIN
- LongIntPtr(destination)^ := LongIntPtr(source)^; { Move full 4 bytes across. }
- IF shadow THEN LongIntPtr(vbrDestination)^ := LongIntPtr(source)^;
- END;
- END;
- END; { Case theSize }
- END;
- END; { EmulateAccess }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { This bus error handler will take the interrupts as they come in, and the procedure
- definition is set up to use the stack frame left on the stack, as it is entered.
- This is fairly weird, but I wanted to
- see how far I could push the high-level language use. There are actually two frame
- types I am concerned about, the $A and $B frames, and they are different lengths.
- I've defined the stack parameters as the larger frame, and then when the smaller
- frame is there, I won't use the extended fields. The routine exits by an assembly
- routine anyway, so the frame won't need to get stripped off the stack. I have to
- do an RTE to restart the CPU, and that's just too dang hard to do from up here.
- I put the small routines into the .a file instead of doing Inline code, even though
- I get extra JSRes. This isn't run often so the easier to read Asm file is the higher
- priority. }
-
- { Since theFrame is bigger than 4 bytes, it will be passed by address, and the assembly
- glue set it up that way. When this routine is executed, the BusErrorGlue has been
- run, which has saved the scratch registers, turned off interrupts, and set up A5 to
- be the dcmd's A5. theFrame is passed in as a pointer, so that Pascal won't make a
- local copy of the entire record as a local. The BusErrorGlue just passes the pointer.
-
- Can't just save and restore the protection byte, it may be changed while in the Debug
- Str, to a new value. }
- PROCEDURE BusError (theFrame: BusErrorFramePtr);
-
- VAR doDisplay: Boolean;
- I: Integer;
-
- BEGIN
- { Got here from BusErrorGlue, and have saved registers, restored A5, and bailed
- if it wasn't an MMU related bus error. }
-
- { If Macsbug is currently executing, then I don't want to call DebugStr, since that will
- cause a hang; Macsbug isn't reentrant. Also, since I don't want to stop if they
- haven't turned Blat on, I need to check that global. I will still take bus errors
- in order to keep the VBR page up to date. }
- IF MacsbugIsNotActive & pDisplayError THEN BEGIN
-
- { Loop for all the ignore addresses, to see if any match. }
- doDisplay := True;
- FOR I := 1 TO pIgnoreCount DO
- IF pIgnoreArray[I] = theFrame^.PC THEN BEGIN
- doDisplay := False;
- Leave; { If one matched, skip out of loop. }
- END;
-
- { If there was no matching address, this one needs to be displayed. }
- IF doDisplay THEN BEGIN
- {always draw stuff, even if in Auto-learn mode, we will only break if not in auto mode}
- dcmdDrawLine('===========================');
- dcmdDrawLine ('--Blat Bus Error: ');
- dcmdDrawLine (ConCat('--address: ', NumberToHex (theFrame^.FaultAddress)));
-
- {jf - if learn or auto-learn is on, add it to the learn list, let the user know if autoLearn is on}
- IF ((pLearn = kOn) OR (pAutoLearn = kOn)) THEN
- AddToIgnores(theFrame^.PC);
-
-
- {only break into MacsBug if auto-learn is off}
- IF pAutoLearn = kOff THEN BEGIN
- IF pUseIP = kUseIP THEN
- DebugStr (ConCat(';ip ', NumberToHex (theFrame^.PC)))
- ELSE
- DebugStr (ConCat(';il ', NumberToHex (theFrame^.PC)));
- END;
-
- End;
- End; { Not in Macsbug and needed to stop. }
-
- { Clear the rerun bit in the Special Status Word, so that when the RTE is hit, it
- won't try to rerun the bus access. Since I'm emulating the access, I have already
- cleared the bus error condition, so there's no need to rerun it. This mask is just
- to clear the 8th bit in the word. }
- theFrame^.SSW := BAnd (theFrame^.SSW, $FEFF);
-
- { Turn off the memory protection since I'm going to do the emulate access anyway,
- and it has to be off to keep this from bus erroring. }
- TurnProtectionOff;
-
- With theFrame^ DO
- EmulateAccess(SSW, FaultAddress, @OutputBuffer, @InputBuffer);
-
- TurnProtectionOn; { Restore to write protect, or read protect. }
- End; { BusError }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Verify that the machine is one that I can run on without wedging. For example, I don't
- run on IIci, or 040 machines, so I gotta say no here, instead of pounding.
- Checks for IIci on board video class machine,
- no 040 machines,
- no VM running. }
- FUNCTION VerifyMachine: Boolean;
-
- VAR err: Integer;
- response: LongInt;
-
- { Sure, this is like a Goto, but it should be obvious how it works. This is the way
- out of here when there is an error, and sets the error string in a common way. I
- prefer this error routine style to massively nested If Then Else structures. }
- PROCEDURE ErrorExit (theErr: Str255);
- BEGIN
- VerifyMachine := False; { Return failing result. }
- pErrorString := theErr;
- Exit (VerifyMachine); { Jump out of enclosing routine. }
- END;
-
- BEGIN
- { See if the VBR vector page made it inside of the 64K max that my tables allow. }
- IF (pVBRTable = NIL) | (ORD(pVBRTable) > $10000) THEN
- ErrorExit (' System heap too large, VBR allocation failed.');
-
- { If VM is installed, or we can't get info on it, set that as an error string. }
- err := Gestalt(gestaltVMAttr, response);
-
- { Allow through gestaltUndefSelectorErr, as not a real Gestalt error. }
- IF ((err <> noErr) & (err <> gestaltUndefSelectorErr)) THEN
- ErrorExit (' Gestalt error');
-
- { If gestaltUndefSelectorErr Then VM isn't installed. Test bit if valid result. }
- IF ((err = noErr) & BTst(response, gestaltVMPresent)) THEN
- ErrorExit (' Blat is incompatible with VM');
-
-
- { If this is an 040 machine, then complain about that. If the selector is undefined,
- I have to assume this isn't an 040. }
- err := Gestalt(gestaltProcessorType, response);
- IF ((err <> noErr) & (err <> gestaltUndefSelectorErr)) THEN
- ErrorExit (' Gestalt error');
- IF ((err = noErr) & (response = gestalt68040)) THEN
- ErrorExit (' Blat is incompatible with the 68040 MMU.');
-
- { If you don't have an MMU, clearly this isn't going to work. If I get an undefined
- selector error, I have to assume one isn't installed. }
- err := Gestalt(gestaltMMUType, response);
- IF ((err <> noErr) & (err <> gestaltUndefSelectorErr)) THEN
- ErrorExit (' Gestalt error');
- IF ((err = gestaltUndefSelectorErr) | (response = gestaltNoMMU)) THEN
- ErrorExit (' Blat requires an MMU in order to function.');
-
- { If this is one of the IIci class machines, with onboard video, I can't run, since their
- tables are too complicated for me. If I get an undefined gestalt selector here,
- something is really screwed up. }
- err := Gestalt(gestaltMachineType, response);
- IF (err <> noErr) THEN
- ErrorExit (' Gestalt error');
- IF ((response = gestaltMacIIci) | (response = gestaltMacIIsi) | (response = gestaltMacLC)) THEN
- ErrorExit (' Blat cannot run on machines that support onboard video (IIci, IIsi, LC).');
-
- { Finally, if I exit normally, then the string will be emptied, since it looks like we I run. }
- pErrorString := '';
- VerifyMachine := True;
- END; { VerifyMachine }
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Save and install the new bus error handler for the system. This is my bus error handler
- which will handle all the MMU faults, and feed through the errors to the old handler.
- The old handler is maintained in the gStandardBusError as I take writes to 8. }
- PROCEDURE InstallBusErrorHandler;
-
- VAR vbrLand: LongIntPtr;
-
- BEGIN
- { Save off the current bus error handler in 8, to init our var. Init the
- VBR based one to point at my handler, since that is the normal case. }
- gStandardBusError := LongIntPtr(BusErrVct)^;
- gStandardVBRBusError := ORD(@BusErrorGlue);
-
- { Install a new bus error handler into the vector page, to point to my routine instead.
- By installing after the VBR is set up to high dcmd area, I install my bus error
- handler only at the VBR vector table. If a bus error comes through that isn't
- related to memory protection, it gets fed on through to the vector that is 0 based. }
- vbrLand := LongIntPtr(ORD(GetVBR)+BusErrVct);
- LongIntPtr(vbrLand)^ := ORD(@BusErrorGlue);
-
- END; { InstallBusErrorHandler }
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Set up the VBR based vector table, and set that table as the new VBR in active use.
- The fundamental reason for doing this is so that I can set Read protection on the 0
- based version of the vectors. VBR use makes it possible to survive that. }
- PROCEDURE InstallVBR;
-
- BEGIN
-
- { Round up the address in pVBRTable so that it is page aligned (low byte zero), and thus
- will be at a 256 byte page boundary. Copy the vectors from zero up to there, to
- start the shadow copy of vectors. This is where I will set the
- VBR register to point, so it will be the real vectors in use. The bytes at zero will
- thus no longer be used for the vectors. }
- pVBRTable := Ptr(ORD(pVBRTable) + 256);
- pVBRTable := Ptr(BAND (ORD(pVBRTable), $FFFFFF00));
- BlockMove (NIL, pVBRTable, 256);
- SetVBR (pVBRTable);
-
- END; { InstallVBR }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
-
- { When the user asks to install, this routine will take the globals saved off, and install
- them as the current MMU tables. By setting up the CRP register to be my tables, that
- installs them and the proper entry is marked as invalid. }
- PROCEDURE InstallTables;
-
- TYPE Int64BitPtr = ^Int64Bit;
-
- VAR crpPointer: Int64BitPtr;
- tcPointer: LongIntPtr;
- VAR xx: SignedByte;
-
- BEGIN
- { While fooling with the MMU registers, I want to avoid screwing up any interrupt
- routines that expect Ram to be mapped when they run. No interrupts while changing. }
-
- { Set the Translation Control register to 0, to disable the MMU. This has to be done to
- allow a change to the CRP. Next change the Cpu Root Pointer to be our new tables,
- that are installed in the code. After that, restore the TC to its new value, that matches
- the new tables, and is enabled. When it turns back on the Address Translation Cache
- will be flushed automatically by the MMU. }
- IF Is32BitMode Then SetMMURegisters (pRegisters32)
- ELSE SetMMURegisters (pRegisters24);
-
- { Install the two register values at the MMU24Info in low memory, for SwapMMUMode.
- MMUTbl is the old name for MMU24Info, but is the only one defined in the interface.
- I made my own constant that is the better name.
- This just installs a new pointer to the variables I use, since on a IIx, they point into
- ROM to start with, and I can't just change the values. By setting them to point to
- me in RAM, the IIx will just use those numbers and not reset them. The order
- of them is important, which is why I made them into a record. }
- LongIntPtr(kMMU24Info)^ := ORD(@pRegisters24);
- LongIntPtr(kMMU32Info)^ := ORD(@pRegisters32);
-
- { *** debug operation to find it easily on emulator. }
- xx := Ptr(5)^;
-
-
- { Find the protection address for the VBR table itself. Bump up by three, to make it a
- byte access for the TurnProtectionOff/On. }
- {*** should do a FindMMUEntry for the zero page too, to be symmetric. }
- pVBRProtectAddress := FindMMUEntry (pVBRTable);
- pVBRProtectAddress := Ptr(ORD(pVBRProtectAddress) + 3);
- END; { InstallTables }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { Patch up the MMU tables, to make them fit for MMU use. They have to be quad-long
- word aligned, and the various stages of the tables have to point to the next lower
- leaves in the tree. The 3rd level of the 24 bit table will use the 4th level of the 32
- bit table, since it is specifiying the same Ram. }
- PROCEDURE PatchupTables;
-
- VAR adjustment: LongInt;
- count: LongInt;
- tableLand: LongIntPtr;
- firstLevel: Ptr; { pointer to the FirstLevel table in dcmd code space. }
-
- BEGIN
- { A block in the assembly file has the MMU tables. This code will move the tables
- in memory lower by however many bytes it takes to make it quad-long word aligned. It
- does this by starting at the StartSpace and fudging that address higher by however many
- bytes it takes to make a quad-long aligned address. I do this by adding 16 to the number,
- then clearing the low nibble with the AND. This is rounding up to the next highest
- quad-word boundary. A 16 byte section of zeros is at the start of the tables, to absorb
- the move. }
- count := ORD(GetEndOfTables) - ORD(GetFirstLevel32);
- firstLevel := Ptr(ORD(GetStartSpace) + 16);
- firstLevel := Ptr(BAND (ORD(firstLevel), $FFFFFFF0));
- BlockMove (GetFirstLevel32, firstLevel, count);
-
- { Now screw around with the tables, making them all point to the successive levels,
- as the MMU requires. The bottom nibble of each entry is the DT field as specified
- by the MMU stuff, and I always make it DT=2, for short entries. This makes the
- first entry of the FirstLevel table point to the SecondLevel, and the first entry of
- the SecondLevel table point to the ThirdLevel, and so on. I have to subtract out
- the adjustment for each, since I moved all the tables with BlockMove above. }
- adjustment := ORD(GetFirstLevel32) - ORD(firstLevel); { how far each table moved. }
-
- tableLand := LongIntPtr(ORD(GetFirstLevel32) - adjustment);
- tableLand^ := BOR(ORD(GetSecondLevel32) - adjustment, kShortDT);
-
- tableLand := LongIntPtr(ORD(GetSecondLevel32) - adjustment);
- tableLand^ := BOR(ORD(GetThirdLevel32) - adjustment, kShortDT);
-
- tableLand := LongIntPtr(ORD(GetThirdLevel32) - adjustment);
- tableLand^ := BOR(ORD(GetFourthLevel32) - adjustment, kShortDT);
-
- { Modify the tables for 24-bit mode too, so that they are correct. }
- tableLand := LongIntPtr(ORD(GetFirstLevel24) - adjustment);
- tableLand^ := BOR(ORD(GetSecondLevel24) - adjustment, kShortDT);
-
- { Set the pointer to the third level to point to the same table that is used for
- the fourth level of the 32-bit mode tables. }
- tableLand := LongIntPtr(ORD(GetSecondLevel24) - adjustment);
- tableLand^ := BOR(ORD(GetFourthLevel32) - adjustment, kShortDT);
-
- { Save off the address of the low byte of the first longInt entry for the FourthLevel,
- since that is the spot that turns the protection on and off. This byte is the DT value
- for that page, and with the WP set, will write protect that first page in memory. }
- pProtectAddress := Ptr(ORD(GetFourthLevel32) - adjustment + 3);
-
- { Set up our new CRP number to match that table address. This means writing in the
- write number for the top half which is the table index stuff, which is disabled in my
- case. The low word of that is DT=2 which means short entries for the next level,
- which is pointed at by the low long. The CRP is a double longint. }
- firstLevel := Ptr(ORD(GetFirstLevel24) - adjustment);
- pRegisters24.theCRP.hiLong := $7FFF0002; { index limit turned off, DT=2 }
- pRegisters24.theCRP.loLong := ORD(firstLevel); { pointer to first level tables. }
-
- firstLevel := Ptr(ORD(GetFirstLevel32) - adjustment);
- pRegisters32.theCRP.hiLong := $7FFF0002; { index limit turned off, DT=2 }
- pRegisters32.theCRP.loLong := ORD(firstLevel); { pointer to first level tables. }
-
- { Set up the new TC register, that will be used when my tables are installed. For
- 24-bit mode it's like: 8=enabled, 0=supervisor root pointer and function code
- lookup are disabled, 8=256 byte page size, 8=initial shift of 8 bits, 4=TIA for 4
- bits in the first table, 4=TIB, 8=TIC, 0=TID.
- 32-bit mode: the top 8=enabled, 0=supervisor root pointer and function
- code lookup are disabled, 8=256 byte page size, 0=initial shift is zero bits (32-bit
- mode), 5=TIA bits for first level table, 7=TIB, 4=TIC, 8=TID. }
- pRegisters24.theTC := $80884480;
- pRegisters32.theTC := $80805748;
-
- END; { PatchupTables }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { This installation routine will allocate a block in the system heap for the MMU tables that
- I use to replace the system tables. It will install a bus error handler, and allocate a new
- block for the actual vectors, making them VBR based instead of zero based. This last
- bit is to make it work with Macsbug, and make things more reliable. }
- PROCEDURE CreateBlat;
-
- BEGIN
- { Allocate a VBR table in the system heap. It has to be within the first 64K or my
- tables won't map to it nicely. If it doesn't show up in that range, then barf. The
- +256 is to allow for the roundoff operation of making sure it is allocated on an
- exact 256 byte page boundary. VBR doesn't have to be that way, but I'm allocating
- it there so I can protect the page, and that makes it necessary to align it to a page. }
- pVBRTable := NewPtrSys(256+256);
-
- { Just to be nice, make sure that we can run on this machine, without wedging. If it
- isn't going to work, then skip the install. Looks at pVBRTable validity too. }
- IF NOT VerifyMachine THEN Exit (CreateBlat);
-
- { Make the tables all cool. Fix up the base address, patch in the multiple levels. }
- PatchupTables;
-
- { Save off the current A5, so that it can be used when the bus error handler is called. }
- SaveTheA5;
-
- pIgnoreCount := 0;
-
- { Addresses are only good on IIfx. }
- (* Only good on fx, and you can get them with Auto anyway.
-
- { *** set up a hack list of memory addresses to ignore. }
- pIgnoreArray [1] := $4080DB76; { Heapguts }
- pIgnoreArray [2] := $4080DB98; { Heapguts }
- pIgnoreArray [3] := $4080DC04; { Heapguts }
- pIgnoreArray [4] := $4080DC26; { Heapguts }
- pIgnoreArray [5] := $4080DBF8; { Heapguts, read }
- pIgnoreArray [6] := $4080DB6A; { Heapguts, read }
- pIgnoreArray [7] := $40808D14;
- pIgnoreArray [8] := $40808D20;
- pIgnoreArray [9] := $40808D30;
- pIgnoreArray [10] := $4080C982;
- pIgnoreCount := 10;
- *)
-
- {jf stuff follows}
- pLearn := kOff;
- pAutoLearn := kOff;
- pUseIP := kUseIP;
-
- { Install the new VBR tables, my bus error handle, and my tables as the active MMU tables. }
- InstallVBR;
- InstallBusErrorHandler;
- InstallTables;
-
- { Stuff the starting value for the protection. This is normally set to kWriteProtect, so
- that all writes can be shadowed in the VBR table to keep things running. Start out
- with not displaying the bus errors. }
- pProtectValue := kWriteProtect;
- pDisplayError := False;
- TurnProtectionOn; { Turn on protect bytes. }
-
- END; { CreateBlat }
-
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { When debugging is required, as it often seems to be, maybe this will help, by
- coughing up all the global variables, and some addresses for runtime inspection.
- This particular dcmd cannot be run inside the TestDcmd world. }
- PROCEDURE BlatDebug;
-
- BEGIN
- dcmdDrawLine ('--Blat Debugging Info--');
-
- dcmdDrawLine (ConCat(' Code Start->: ', NumberToHex (@LowerStr255)));
- dcmdDrawLine (ConCat(' gStandardBusError: ', NumberToHex (gStandardBusError)));
- dcmdDrawLine (ConCat(' gStandardVBRBusError: ', NumberToHex (gStandardVBRBusError)));
- dcmdDrawLine (ConCat(' gSavedSR: ', NumberToHex (gSavedSR)));
- dcmdDrawLine (ConCat(' pRegisters24.theCRP: ', NumberToHex (pRegisters24.theCRP.hiLong), NumberToHex (pRegisters24.theCRP.loLong)));
- dcmdDrawLine (ConCat(' pRegisters24.theTC: ', NumberToHex (pRegisters24.theTC)));
- dcmdDrawLine (ConCat(' pRegisters32.theCRP: ', NumberToHex (pRegisters32.theCRP.hiLong), NumberToHex (pRegisters32.theCRP.loLong)));
- dcmdDrawLine (ConCat(' pRegisters32.theTC: ', NumberToHex (pRegisters32.theTC)));
- dcmdDrawLine (ConCat(' pProtectAddress: ', NumberToHex (ORD(pProtectAddress))));
- dcmdDrawLine (ConCat(' pProtectValue: ', NumberToHex (pProtectValue)));
- dcmdDrawLine (ConCat(' pVBRTable: ', NumberToHex (ORD(pVBRTable))));
- dcmdDrawLine (ConCat(' pVBRProtectAddress: ', NumberToHex (pVBRProtectAddress)));
- dcmdDrawLine (ConCat(' pErrorString: ', pErrorString));
- dcmdDrawLine (ConCat(' pIgnoreCount: ', NumberToHex (pIgnoreCount)));
- DumpIgnoresList;
-
- IF pDisplayError THEN
- dcmdDrawLine (' pDisplayError: True')
- ELSE
- dcmdDrawLine (' pDisplayError: False');
- IF pLearn THEN
- dcmdDrawLine (' pLearn: True')
- ELSE
- dcmdDrawLine (' pLearn: False');
- IF pAutoLearn THEN
- dcmdDrawLine (' pAutoLearn: True')
- ELSE
- dcmdDrawLine (' pAutoLearn: False');
- IF pUseIP THEN
- dcmdDrawLine (' pUseIP: True')
- ELSE
- dcmdDrawLine (' pUseIP: False');
- IF Is32BitMode THEN
- dcmdDrawLine (' In 32 Bit Mode')
- ELSE
- dcmdDrawLine (' In 24 Bit Mode');
- IF MacsbugIsNotActive THEN
- dcmdDrawLine (' Macsbug is Not Active')
- ELSE
- dcmdDrawLine (' Macsbug is Active');
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { dump the current settings of blat. }
- PROCEDURE DumpStatusInfo;
-
- BEGIN
- dcmdDrawLine('-------------------------------------');
- IF NOT pDisplayError THEN
- dcmdDrawLine (' Blat is OFF')
- ELSE IF pProtectValue = kWriteProtect THEN
- dcmdDrawLine (' Blat is catching WRITES only')
- ELSE IF pProtectValue = kReadWriteProtect THEN
- dcmdDrawLine (' Blat is catching both READS and WRITES');
-
- IF pLearn = kOn THEN
- dcmdDrawLine (' Blat learning mode is ON')
- ELSE
- dcmdDrawLine (' Blat learning mode is OFF');
-
- IF pAutoLearn = kOn THEN
- dcmdDrawLine (' Blat auto-learning mode is ON')
- ELSE
- dcmdDrawLine (' Blat auto-learning mode is OFF');
-
- IF pUseIP = kUseIP THEN
- dcmdDrawLine (' Blat is using IP')
- ELSE
- dcmdDrawLine (' Blat is using IL');
- dcmdDrawLine('-------------------------------------');
-
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- { If something was not understood, dump the info for how this works. If I couldn't install
- because of an incompatibility, then say that instead, and include the error message so
- they know why. }
- PROCEDURE DumpHelpInfo;
-
- BEGIN
- IF pErrorString = '' THEN BEGIN
- dcmdDrawLine ('Blat Off | Writes | Both | Dump | Ignore $xxx ... | IP | IL | Learn | Auto | Status');
- dcmdDrawLine (' Uses the MMU to catch accesses to bytes 0..255. (Version 5)');
- DumpStatusInfo;
- END
- ELSE BEGIN
- dcmdDrawLine ('Blat is NOT installed.');
- dcmdDrawLine (pErrorString); { Error line if not installed. }
- END;
- END;
-
- {---------------------------------------------------------------------------------------------------------------------------------}
- {---------------------------------------------------------------------------------------------------------------------------------}
-
-
- { This fine fellow is the main entry point for the dcmd. It is the hook by which I get called
- by MacsBug to do my thing. It is basically the chance to key off the command line and do
- what they request.
-
- Change the version number in the help, whenever it is re-released. This is the only
- version number in the program. }
- PROCEDURE CommandEntry (paramPtr: DCmdBlockPtr);
-
- VAR parseChar: CHAR;
- ignoreDude: LongInt;
- okParse: Boolean;
-
- BEGIN
- CASE paramPtr^.request OF
- { When I'm called to Init, do the init code of setting up the MMU tables. }
- dcmdInit:
- CreateBlat;
-
- { I can get various DoIt commands, so parse out the options. If I don't get anything,
- do the standard dump help info. I lowercase the string so I can avoid any case sensitivity
- on options passed in. }
- dcmdDoIt:
- BEGIN
- parseChar := dcmdGetNextParameter (pDumpString);
- LowerStr255 (pDumpString);
-
- IF pDumpString = 'writes' THEN BEGIN
- pDisplayError := True; { Stop on errors, to show them. }
- pProtectValue := kWriteProtect;
- TurnProtectionOn; { Set bytes to Write Protect. }
- dcmdDrawLine (' Blat will catch all writes to memory from 0..255.');
- END
-
- ELSE IF pDumpString = 'both' THEN BEGIN
- pDisplayError := True; { Stop on errors, to show them. }
- pProtectValue := kReadWriteProtect;
- TurnProtectionOn; { Set bytes to Invalid (R/W protect). }
- dcmdDrawLine (' Blat will catch both reads and writes to memory from 0..255.');
- END
-
- ELSE IF pDumpString = 'off' THEN BEGIN
- pDisplayError := False; { don't stop on bus errors. }
- pProtectValue := kWriteProtect;
- TurnProtectionOn; { Protection still on, to maintain VBR.}
- dcmdDrawLine (' Blat is off');
- END
-
- ELSE IF pDumpString = 'dump' THEN BEGIN
- DumpIgnoresList;
- END
-
- {jf stuff follows}
- ELSE IF pDumpString = 'ip' THEN BEGIN
- dcmdDrawLine (' Blat will use IP');
- pUseIP := kUseIP;
- END
-
- ELSE IF pDumpString = 'il' THEN BEGIN
- dcmdDrawLine (' Blat will use IL');
- pUseIP := kUseIL;
- END
-
- ELSE IF pDumpString = 'learn' THEN BEGIN
- IF pLearn = kOn THEN BEGIN
- pLearn := kOff;
- dcmdDrawLine(' Blat learn mode OFF');
- END
- ELSE BEGIN
- pLearn := kOn;
- pAutoLearn := kOff;
- dcmdDrawLine(' Blat learn mode ON (auto-learn is OFF)');
- END;
- END
-
- ELSE IF pDumpString = 'auto' THEN BEGIN
- IF pAutoLearn = kOn THEN BEGIN
- pAutoLearn := kOff;
- dcmdDrawLine(' Blat auto-learn mode OFF');
- END
- ELSE BEGIN
- pAutoLearn := kOn;
- pLearn:= kOff;
- dcmdDrawLine(' Blat auto-learn mode ON (learn is OFF)');
- END;
- END
-
- ELSE IF pDumpString = 'ignore' THEN BEGIN
- REPEAT {loop through and get all of the addresses to ignore}
- parseChar := dcmdGetNextExpression (ignoreDude, okParse);
- IF okParse THEN AddToIgnores (ignoreDude);
- UNTIL not okParse;
- END
-
- ELSE IF pDumpString = 'status' THEN
- DumpHelpInfo
-
- ELSE IF pDumpString = 'debug' THEN BEGIN
- BlatDebug;
- END
-
- ELSE DumpHelpInfo;
- END;
-
- { Give them the obvious help info. }
- OTHERWISE
- DumpHelpInfo;
-
- END; { End of case paramPtr^.request. }
-
- END; { CommandEntry }
-
- END.
-